# Learn YueScript in 15 Minutes

:::tip Note
This tutorial provides a quick overview of [YueScript](https://yuescript.org), a modern alternative to Lua. YueScript inherits Lua's features while introducing syntax sugar and a Python-like coding style. Since YueScript is a dialect of MoonScript, this tutorial is a modified version of ["Learn MoonScript in 15 Minutes"](https://github.com/leafo/moonscript/wiki/Learn-MoonScript-in-15-Minutes), and also assumes you have some programming experience and are familiar with Lua, you can refer to the [Lua official documentation](https://lua-users.org/wiki/TutorialDirectory) for learning.
:::

YueScript does not use 'do', 'then', or 'end' like Lua would and instead uses an indented syntax, much like Python.

```yue
-- Two dashes start a comment. Comments can go until the end of the line.
-- YueScript transpiled to Lua does not keep comments.
```

## 1. Assignment

```yue
hello = "world"
a, b, c = 1, 2, 3
hello = 123 -- Overwrites `hello` from above.

x = 0
x += 10 -- x = x + 10

s = "hello "
s ..= "world" -- s = s .. "world"

b = false
b and= true or false -- b = b and (true or false)
```

## 2. Literals and Operators

Literals work almost exactly as they would in Lua. Strings can be broken in the middle of a line without requiring a \\.

```yue
some_string = "exa
mple" -- local some_string = "exa\nmple"
```

Strings can also have interpolated values, or values that are evaluated and then placed inside of a string.

```yue
some_string = "This is an #{some_string}" -- Becomes 'This is an exa\nmple'

### 2.1. Function Literals

Functions are written using arrows:

```yue
my_function = -> -- compiles to `function() end`
my_function() -- calls an empty function
```

Functions can be called without using parenthesis. Parentheses may still be used to have priority over other functions.

```yue
func_a = -> print "Hello World!"
func_b = ->
	value = 100
	print "The value: #{value}"
```

If a function needs no parameters, it can be called with either `()` or `!`.

```yue
func_a!
func_b()
```

Functions can use arguments by preceding the arrow with a list of argument names bound by parentheses.

```yue
sum = (x, y) -> x + y -- The last expression is returned from the function.
print sum(5, 10)
```

Lua has an idiom of sending the first argument to a function as the object, like a 'self' object. Using a fat arrow (=>) instead of a skinny arrow (->) automatically creates a `self` variable. `@x` is a shorthand for `self.x`.

```yue
func = (num) => @value + num
```

Default arguments can also be used with function literals:

```yue
a_function = (name = "something", height = 100) ->
	print "Hello, I am #{name}.\nMy height is #{height}."
```

Because default arguments are calculated in the body of the function when transpiled to Lua, you can reference previous arguments.

```yue
some_args = (x = 100, y = x + 1000) -> print(x + y)
```

Considerations

The minus sign plays two roles, a unary negation operator and a binary subtraction operator. It is recommended to always use spaces between binary operators to avoid the possible collision.

```yue
a = x - 10 --  a = x - 10
b = x-10 -- b = x - 10
c = x -y -- c = x(-y)
d = x- z -- d = x - z
```

When there is no space between a variable and string literal, the function call takes priority over following expressions:

```yue
x = func"hello" + 100 -- func("hello") + 100
y = func "hello" + 100 -- func("hello" + 100)
```

Arguments to a function can span across multiple lines as long as the arguments are indented. The indentation can be nested as well.

```yue
my_func 5, -- called as my_func(5, 8, another_func(6, 7, 9, 1, 2), 5, 4)
	8, another_func 6, 7, -- called as
		9, 1, 2,		  -- another_func(6, 7, 9, 1, 2)
	5, 4
```

If a function is used at the start of a block, the indentation can be different than the level of indentation used in a block:

```yue
if func 1, 2, 3, -- called as func(1, 2, 3, "hello", "world")
		"hello",
		"world"
	print "hello"
```

## 3. Tables

Tables are defined by curly braces, like Lua:

```yue
some_values = [1, 2, 3, 4] -- better uses `[]` instead of `{}` for array-like tables
```

Tables can use newlines instead of commas.

```yue
some_other_values = [
	5, 6
	7, 8
]
```

Assignment is done with `:` instead of `=`:

```yue
profile = {
	name: "Bill"
	age: 200
	"favorite food": "rice"
}
```

Curly braces can be left off for `key: value` tables.

```yue
y = type: "dog", legs: 4, tails: 1

profile =
	height: "4 feet",
	shoe_size: 13,
	favorite_foods: -- nested table
		foo: "ice cream",
		bar: "donuts"

my_function dance: "Tango", partner: "none" -- :( forever alone
```

Tables constructed from variables can use the same name as the variables by using `:` as a prefix operator.

```yue
hair = "golden"
height = 200
person = {:hair, :height}
```

Like in Lua, keys can be non-string or non-numeric values by using `[]`.

```yue
t =
	[1 + 2]: "hello"
	"hello world": true -- Can use string literals without `[]`.
```

### 3.1. Table Comprehensions

List Comprehensions

Creates a copy of a list but with all items doubled. Using a star before a variable name or table can be used to iterate through the table's values.

```yue
items = [1, 2, 3, 4]
doubled = [item * 2 for item in *items]
```

Uses `when` to determine if a value should be included.

```yue
slice = [item for item in *items[1, 2] when item % 2 == 0]
```

`for` clauses inside of list comprehensions can be chained.

```yue
x_coords = [4, 5, 6, 7]
y_coords = [9, 2, 3]

points = [ [x, y] for x in *x_coords for y in *y_coords]
```

Numeric for loops can also be used in comprehensions:

```yue
evens = [i for i = 1, 100 when i % 2 == 0]
```

Table Comprehensions are very similar but use `{` and `}` and take two values for each iteration.

```yue
thing = color: "red", name: "thing", width: 123
thing_copy = {k, v for k, v in pairs thing}
```

Tables can be "flattened" from key-value pairs in an array by using `unpack` to return both values, using the first as the key and the second as the value.

```yue
tuples = [ ["hello", "world"], ["foo", "bar"]]
table = {unpack tuple for tuple in *tuples}
```

Slicing can be done to iterate over only a certain section of an array. It uses the `*` notation for iterating but appends `[start, end, step]`.

The next example also shows that this syntax can be used in a `for` loop as well as any comprehensions.

```yue
for item in *points[1, 10, 2]
	print unpack item
```

Any undesired values can be left off. The second comma is not required if the step is not included.

```yue
words = ["these", "are", "some", "words"]
for word in *words[,3]
	print word
```

## 4. Control Structures

```yue
have_coins = false
if have_coins
	print "Got coins"
else
	print "No coins"
```

Use `then` for single-line `if`

```yue
result = if have_coins then "Got coins" else "No coins"
```

`unless` is the opposite of `if`

```yue
unless os.date("%A") == "Monday"
	print "It is not Monday!"
```

`if` and `unless` can be used as expressions

```yue
is_tall = (name) -> if name == "Rob" then true else false
message = "I am #{if is_tall "Rob" then "very tall" else "not so tall"}"
print message -- "I am very tall"
```

`if`, `elseif`, and `unless` can evaluate assignment as well as expressions.

```yue
if x := possibly_nil! -- sets `x` to `possibly_nil()` and evaluates `x`
	print x
```

Conditionals can be used after a statement as well as before. This is called a "line decorator".

```yue
is_monday = os.date("%A") == "Monday"
print("It IS Monday!") if isMonday
print("It is not Monday..") unless isMonday
-- print("It IS Monday!" if isMonday) -- Not a statement, does not work
```

### 4.1 Loops

```yue
for i = 1, 10
	print i

for i = 10, 1, -1 do print i -- Use `do` for single-line loops.

i = 0
while i < 10
	continue if i % 2 == 0 -- Continue statement; skip the rest of the loop.
	print i
```

Loops can be used as a line decorator, just like conditionals

```yue
print "item: #{item}" for item in *items
```

Using loops as an expression generates an array table. The last statement in the block is coerced into an expression and added to the table.

```yue
my_numbers = for i = 1, 6 do i -- [1, 2, 3, 4, 5, 6]

-- use `continue` to filter out values
odds = for i in *my_numbers
	continue if i % 2 == 0 -- acts opposite to `when` in comprehensions!
	i -- Only added to return table if odd
```

A `for` loop returns `nil` when it is the last statement of a function. Use an explicit `return` to generate a table.

```yue
print_squared = (t) -> for x in *t do print x * x -- returns `nil`
squared = (t) -> return for x in *t do x * x -- returns new table of squares
```

The following does the same as `(t) -> [i for i in *t when i % 2 == 0]`.
But list comprehension generates better code and is more readable!

```yue
filter_odds = (t) ->
	return for x in *t
		if x % 2 == 0 then x else continue
evens = filter_odds(my_numbers) -- [2, 4, 6]
```

### 4.2 Switch Statements

Switch statements are a shorthand way of writing multiple `if` statements checking against the same value. The value is only evaluated once.

```yue
name = "Dan"

switch name
	when "Dave"
		print "You are Dave."
	when "Dan"
		print "You are not Dave, but Dan."
	else
		print "You are neither Dave nor Dan."
```

Switches can also be used as expressions, as well as compare multiple values. The values can be on the same line as the `when` clause if they are only one expression.

```yue
b = 4
next_even = switch b
	when 1 then 2
	when 2, 3 then 4
	when 4, 5 then 6
	else error "I can't count that high! D:"
```

## 5. Object Oriented Programming

Classes are created using the `class` keyword followed by an identifier, typically written using CamelCase. Values specific to a class can use @ as the identifier instead of `self.value`.

```yue
class Inventory
	new: => @items = {}
	add_item: (name) => -- note the use of fat arrow for classes!
		@items[name] = 0 unless @items[name]
		@items[name] += 1
```

The `new` function inside of a class is special because it is called when an instance of the class is created.

Creating an instance of the class is as simple as calling the class as a function. Calling functions inside of the class uses \ to separate the instance from the function it is calling.

```yue
inv = Inventory!
inv\add_item "t-shirt"
inv\add_item "pants"
```

Values defined in the class - not the new() function - will be shared across all instances of the class.

```yue
class Person
	clothes: []
	give_item: (name) =>
		table.insert @clothes, name

a = Person!
b = Person!

a\give_item "pants"
b\give_item "shirt"

-- prints out both "pants" and "shirt"
print item for item in *a.clothes
```

Class instances have a value `.__class` that are equal to the class object that created the instance.

```yue
assert b.__class == Person
```

Variables declared in class body the using the `=` operator are locals, so these "private" variables are only accessible within the current scope.

```yue
class SomeClass
	x = 0
	reveal: ->
		x += 1
		print x

a = SomeClass!
b = SomeClass!
print a.x -- nil
a.reveal! -- 1
b.reveal! -- 2
```

### 5.1 Inheritance

The `extends` keyword can be used to inherit properties and methods from another class.

```yue
class Backpack extends Inventory
	size: 10
	add_item: (name) =>
		error "backpack is full" if #@items > @size
		super name -- calls Inventory.add_item with `name`.
```

Because a `new` method was not added, the `new` method from `Inventory` will be used instead. If we did want to use a constructor while still using the constructor from `Inventory`, we could use the magical `super` function during `new()`.

When a class extends another, it calls the method `__inherited` on the parent class (if it exists). It is always called with the parent and the child object.

```yue
class ParentClass
	@__inherited: (child) =>
		print "#{@__name} was inherited by #{child.__name}"
	a_method: (a, b) => print a .. ' ' .. b
```

Will print 'ParentClass was inherited by MyClass'

```yue
class MyClass extends ParentClass
	a_method: =>
		super "hello world", "from MyClass!"
		assert super == ParentClass
```

## 6. Scope

All values are local by default. The `global` keyword can be used to declare the variable as a global value.

```yue
global var_1, var_2
var_1, var_3 = "hello", "world" -- var_3 is local, var_1 is not.

global this_is_global_assignment = "Hi!"
```

Classes can also be prefixed with `global` to make them global classes. Alternatively, all CamelCase variables can be exported automatically using `global ^`, and all values can be globals using `global *`.

`do` lets you manually create a scope, for when you need local variables.

```yue
do
	x = 5
print x -- nil
```

Here we use `do` as an expression to create a closure.

```yue
counter = do
	i = 0
	->
		i += 1
		return i

print counter!  -- 1
print counter!  -- 2
```

The `local` keyword can be used to define variables before they are assigned.

```yue
local var_4
if something
	var_4 = 1
print var_4 -- works because `var_4` was set in this scope, not the `if` scope.
```

The `local` keyword can also be used to shadow an existing variable.

```yue
x = 10
if false
	local x
	x = 12
print x -- 10
```

Use `local *` to forward-declare all variables. Alternatively, use `local ^` to forward-declare all CamelCase values.

```yue
local *

first = ->
	second!

second = ->
	print data

data = {}
```

### 6.1 Import

Import functions and variables from another module.

```yue
import "mod" -- Is short for `const mod = require "mod"`.
import "mod" as :func, :var -- Is short for `const {:func, :var} = require "mod"`.
```

### 6.2 With

The `with` statement can be used to quickly call and assign values in an instance of a class or object.

```yue
file = with File "lmsi15m.yue" -- `file` is the value of `set_encoding()`.
	\set_encoding "utf8"

create_person = (name, relatives) ->
	with Person!
		.name = name
		\add_relative relative for relative in *relatives
me = create_person "Ryan", ["sister", "sister", "brother", "dad", "mother"]

with str = "Hello" -- assignment as expression! :D
	print "original: #{str}"
	print "upper: #{\upper!}"
```

### 6.3 Destructuring

Destructuring can take arrays, tables, and nested tables and convert them into local variables.

```yue
obj2 =
	numbers: [1, 2, 3, 4]
	properties:
		color: "green"
		height: 13.5

{numbers: [first, second], properties: {:color}} = obj2

print first, second, color -- 1 2 green
```

`first` and `second` return [1] and [2] because they are as an array, but `:color` is like `color: color` so it sets itself to the `color` value.

Destructuring can be used in place of `import`.

```yue
{:max, :min, random: rand} = math -- rename math.random to rand
```

Destructuring can be done anywhere assignment can be done.

```yue
for [left, right] in *[ ["hello", "world"], ["egg", "head"]]
	print left, right
```

:::tip Further Reading
For complete YueScript documentation, visit the [official YueScript website](https://yuescript.org/doc).
:::
